package org.evolver.disser;

import org.apfloat.Apfloat;
import org.apfloat.ApfloatMath;

public class InputData {
//  Calculates the loss in the waveguide
    public Apfloat TgDeltaWg(Apfloat epsRealIn) {
//        Real InputData::TgDeltaWg(const Real& epsRealIn)
//         {
//         Real    c1 = 1 - lambda_over_lambda_cr_sq;
//         Real    c2Nominator = at_over_2bt + lambda_over_lambda_cr_sq / epsRealIn;
//         Real    c2Denominator = at_over_2bt + lambda_over_lambda_cr_sq;
//
//         return  c1 * ( c2Nominator / c2Denominator ) * wg_q;
//         }
        Apfloat c1 = APFLOAT_ONE.subtract(lambda_over_lambda_cr_sq);
        Apfloat c2Nominator = at_over_2bt.add(lambda_over_lambda_cr_sq.divide(epsRealIn));
        Apfloat c2Denominator = at_over_2bt.add(lambda_over_lambda_cr_sq);
        return  c1.multiply(wg_q).multiply(c2Nominator).divide(c2Denominator);
    }
 
//  Calculates measured loss in sample
    public Apfloat	TgDeltaSmMeas(Apfloat epsMeasIn) {
//        Real    bd = BetaByEps(epsMeasIn) * sm_dt;
//        Real    tg_bd = tan ( bd );
//        Real    tg_bd_sq = tg_bd * tg_bd;
//        Real    eq1_rhs_tg_sq = eq1_rhs_tg * eq1_rhs_tg;
//
//        Real    c1 = lambda_g * K / ( M_PI * sm_dt );
//
//        Real    c2Nominator = 1 + eq1_rhs_tg_sq;
//        Real    c2Denominator = 1 + tg_bd_sq - tg_bd / bd;
//
//        Real    c3 = 1 - lambda_over_lambda_cr_sq / epsMeasIn;
//
//        return c1 * ( c2Nominator / c2Denominator ) * c3 - TgDeltaWg ( epsMeasIn );
        Apfloat bd = BetaByEps(epsMeasIn).multiply(sm_dt);
        Apfloat tg_bd = ApfloatMath.tan(bd);
        Apfloat tg_bd_sq = tg_bd.multiply(tg_bd);
        Apfloat eq1_rhs_tg_sq = eq1_rhs_tg.multiply(eq1_rhs_tg);
        Apfloat c1 = lambda_g.multiply(K).divide(APFLOAT_PI.multiply(sm_dt));
        Apfloat c2Nominator = APFLOAT_ONE.add(eq1_rhs_tg_sq);
        Apfloat c2Denominator = APFLOAT_ONE.add(tg_bd_sq).subtract(tg_bd.divide(bd));
        Apfloat c3 = APFLOAT_ONE.subtract(lambda_over_lambda_cr_sq.divide(epsMeasIn));

    	return (c1.multiply(c3).multiply(c2Nominator).divide(c2Denominator)).subtract(TgDeltaWg(epsMeasIn));
    }

//  Calculates the real loss in the sample
    public Apfloat	TgDeltaSmReal(Apfloat tgdSmMeas, Apfloat epsRealIn) {
//        return tgdSmMeas * exp(epsRealIn * (wg_bt/sm_bt - 1));
        return tgdSmMeas.multiply(ApfloatMath.exp
                       (epsRealIn.multiply(wg_bt.divide(sm_bt).subtract(APFLOAT_ONE))));
	}

	public Apfloat	BetaByEps(Apfloat epsIn) {
//	    return
//        (
//        2 * M_PI *
//        sqrt
//            (
//            (epsIn - 1) * one_over_lambda_cr_sq +
//            epsIn * one_over_lambda_g_sq
//            )
//        );
        Apfloat tmp1 = (epsIn.subtract(APFLOAT_ONE)).multiply(one_over_lambda_cr_sq);
        Apfloat tmp2 = epsIn.multiply(one_over_lambda_g_sq);
        
        return
            APFLOAT_TWO.multiply(APFLOAT_PI).
                multiply(ApfloatMath.sqrt(tmp1.add(tmp2))); 
	}

	public Apfloat	EpsByBeta(Apfloat betaIn) {
//	    return
//        (
//        ( one_over_lambda_cr_sq + betaIn * betaIn / ( 4 * M_PI * M_PI ) ) /
//        ( one_over_lambda_cr_sq + one_over_lambda_g_sq )
//        );
	    Apfloat    nominator   = one_over_lambda_cr_sq.
	                                add(betaIn.multiply(betaIn).divide(APFLOAT_FOUR_PI_SQ));
	    Apfloat    denominator = one_over_lambda_cr_sq.add(one_over_lambda_g_sq);
	    return nominator.divide(denominator);
	}

	InputData(
	        Double  lambdaIn, 
	        Double  wg_a0In, 
	        Double  wg_b0In, 
	        Double  sm_b0In, 
	        Double  sm_d0In, 
	        Double  wg_alphaIn, 
	        Double  sm_alphaIn, 
	        Double  tIn, 
	        Double  delta_lIn, 
	        Double  delta_xtIn, 
	        Double  wg_qIn, 
	        Double  epsMinIn, 
	        Double  epsMaxIn, 
	        Integer precisionIn 
	)
	{
	    lambda						= new Apfloat(lambdaIn);
	    wg_a0						= new Apfloat(wg_a0In);
	    wg_b0						= new Apfloat(wg_b0In);
	    sm_b0						= new Apfloat(sm_b0In);
	    sm_d0						= new Apfloat(sm_d0In);
	    wg_alpha					= new Apfloat(wg_alphaIn);
	    sm_alpha					= new Apfloat(sm_alphaIn);
	    t							= new Apfloat(tIn);
	    delta_l						= new Apfloat(delta_lIn);
	    delta_xt					= new Apfloat(delta_xtIn);
	    wg_q						= new Apfloat(wg_qIn);
	    epsMin						= new Apfloat(epsMinIn);
	    epsMax						= new Apfloat(epsMaxIn);
	    precision					= new Integer(precisionIn * 3);

        APFLOAT_ONE                 = new Apfloat(1.0, precision);
        APFLOAT_TWO                 = new Apfloat(2.0, precision);
        APFLOAT_PI                  = ApfloatMath.pi(precision);
        APFLOAT_FOUR_PI_SQ          = APFLOAT_TWO.multiply(APFLOAT_TWO).
                                        multiply(APFLOAT_PI).multiply(APFLOAT_PI);
        UpdateValues();
	}

	private void UpdateValues() {
		wg_at = wg_a0.multiply((wg_alpha.multiply(t).add(APFLOAT_ONE)));
	    wg_bt = wg_b0.multiply((wg_alpha.multiply(t).add(APFLOAT_ONE)));
	    sm_bt = sm_b0.multiply((sm_alpha.multiply(t).add(APFLOAT_ONE))); 
	    sm_dt = sm_d0.multiply((sm_alpha.multiply(t).add(APFLOAT_ONE)));

//		Initialize temporary variables
	    lambda_cr = wg_at.multiply(APFLOAT_TWO);

	    Apfloat lambda_ratio = lambda.divide(lambda_cr);
	    lambda_over_lambda_cr_sq = lambda_ratio.multiply(lambda_ratio);
	    lambda_g  = lambda.divide(ApfloatMath.sqrt(APFLOAT_ONE.subtract(lambda_ratio.multiply(lambda_ratio))));

	    one_over_lambda_cr_sq = APFLOAT_ONE.divide(lambda_cr.multiply(lambda_cr));
	    one_over_lambda_g_sq  = APFLOAT_ONE.divide(lambda_g.multiply(lambda_g));

	    Apfloat	tmp_K = ApfloatMath.sin(APFLOAT_PI.multiply(delta_xt).divide(lambda_g));
	    K           = tmp_K.divide(ApfloatMath.sqrt(tmp_K.multiply(tmp_K).add(APFLOAT_ONE)));
	    at_over_2bt = wg_at.divide(APFLOAT_TWO.multiply(wg_bt));

	    eq1_rhs_tg = ApfloatMath.tan(APFLOAT_TWO.multiply(APFLOAT_PI).multiply(delta_l.add(sm_dt).divide(lambda_g)));
	}

	public String toString() {
	    StringBuilder result = new StringBuilder();
	    String NEW_LINE = System.getProperty("line.separator");

	    Apfloat    ten = new Apfloat(100);
	    
	    result.append("lambda: " + lambda.multiply(ten).truncate().divide(ten).toString(true) + NEW_LINE);

	    return result.toString();
	}
	
	// These values are entered
	private Apfloat  lambda;         // free-space wave length
    
    private Apfloat  wg_a0;          // initial width of a waveguide window
    private Apfloat  wg_b0;          // initial height of a waveguide window
    private Apfloat  sm_b0;          // initial height of a sample
    private Apfloat  sm_d0;          // initial depth of a sample
    
    private Apfloat  wg_alpha;       // coeff. of thermal expansion of wg
    private Apfloat  sm_alpha;       // coeff. of thermal expansion of a sm
    private Apfloat  t;              // temperature
    
    private Apfloat  delta_l;        // probe displacement
    private Apfloat  delta_xt;       // "double local minima" width
    private Apfloat  wg_q;           // waveguide damping coeff.

    private Apfloat  epsMin;		 // start search here
    private Apfloat  epsMax;		 // end search here
    private Integer  precision;		 // precision of dihotomy

    // These values are computed
    private Apfloat  wg_at;          // width of a heated waveguide window
    private Apfloat  wg_bt;          // height of a heated waveguide window
    private Apfloat  sm_bt;          // height of a heated sample
    private Apfloat  sm_dt;          // depth of a heated sample
    private Apfloat  lambda_cr;
    private Apfloat  lambda_g;
    private Apfloat  one_over_lambda_cr_sq;
    private Apfloat  one_over_lambda_g_sq;
    private Apfloat  lambda_over_lambda_cr_sq;
    private Apfloat  K;
    private Apfloat  at_over_2bt;
    private Apfloat  eq1_rhs_tg;
    
    private final Apfloat   APFLOAT_ONE;
    private final Apfloat   APFLOAT_TWO;
    private final Apfloat   APFLOAT_PI;
    private final Apfloat   APFLOAT_FOUR_PI_SQ;
}
